<?php

/**
 * ORM 字段解析器
 *
 * @author     jonwang(jonwang@myqee.com)
 * @category   MyQEE
 * @package    System
 * @subpackage Core
 * @copyright  Copyright (c) 2008-2011 myqee.com
 * @license    http://www.myqee.com/license.html
 */
abstract class MyQEE_OOP_ORM_Parse
{

    public function __construct()
    {

    }

    /**
     * 检查并规范ORM字段配置
     *
     */
    public static function check_config(&$setting, &$field_to_offset, &$id_field)
    {
        $field_to_key = array();
        if ( is_array($setting) )
        {
            foreach ( $setting as $key => &$field_config )
            {
                if ( ! is_array($field_config) )
                {
                    if ( null === $field_config ) $field_config = $key;
                    $field_config = array('field_name' => $field_config);
                }

                OOP_ORM_Parse::_check_fieldname($key, $field_config, $field_to_offset);
                OOP_ORM_Parse::_check_orm($key, $field_config);
                OOP_ORM_Parse::_check_data($key, $field_config);
                OOP_ORM_Parse::_check_object($key, $field_config);

                if ( ! $id_field )
                {
                    OOP_ORM_Parse::_check_id_field($key, $field_config, $id_field);
                }

                $field_config['is_readonly'] = isset($field_config['is_readonly']) ? (bool)$field_config['is_readonly'] : false;
            }
            # 整理数据子集
            unset($field_config);
            foreach ( $setting as $key => $field_config )
            {
                if ( isset($field_config['parent_field_name']) )
                {
                    $setting[$field_config['parent_field_name']]['all_sub_fields'][] = $key;
                }
            }

        }

    }

    /**
     * 检查字段名
     *
     * @param string $key
     * @param array $field_config
     */
    protected static function _check_fieldname($key, &$field_config, &$field_to_offset)
    {
        if ( isset($field_config['is_virtual_field']) && $field_config['is_virtual_field'] )
        {
            unset($field_config['field_name']);
            return;
        }
        elseif ( ! isset($field_config['field_name']) )
        {
            $field_config['field_name'] = $key;
        }
        $field_config['is_virtual_field'] = false;
        if ( strpos($field_config['field_name'], '[') && preg_match('#^([a-z0-9_]+)\[(.*)\]$#i', $field_config['field_name'], $m) )
        {
            unset($field_config['field_name'], $field_config['data'], $field_config['orm']);
            $field_config['parent_field_name'] = $m[1];
            $field_config['sub_field'] = str_replace('][', '.', $m[2]);
        }
        $field_to_offset[$field_config['field_name']][] = $key;
    }

    /**
     * 检查是否ID字段
     *
     * @param string $key
     * @param array $field_config
     */
    protected static function _check_id_field($key, &$field_config, &$id_field)
    {
        if ( isset($field_config['is_id_field']) && true === $field_config['is_id_field'] )
        {
            $id_field = $field_config['field_name'];
        }
    }

    /**
     * 检查ORM配置
     *
     * @param string $key
     * @param array $field_config
     */
    protected static function _check_orm($key, &$field_config)
    {
        if ( ! isset($field_config['orm']) )
        {
            return;
        }
        if ( $field_config['orm'] && ! is_array($field_config['orm']) )
        {
            $field_config['orm'] = array('name' => (string)$field_config['orm']['name']);
        }
        elseif ( ! isset($field_config['orm']['name']) )
        {
            unset($field_config['orm']);
            return;
        }

        # 设置为ORM时，data,object,format配置将无效
        unset($field_config['data'], $field_config['object'], $field_config['format']);
    }

    /**
     * 检查获取数据配置
     *
     * @param string $key
     * @param array $field_config
     * @throws Exception
     */
    protected static function _check_data($key, &$field_config)
    {
        if ( ! isset($field_config['data']) )
        {
            return;
        }
        if ( ! is_array($field_config['data']) )
        {
            Core::debug()->error('ORM字段配置错误，data属性应该为数组');
            unset($field_config['data']);
            return;
        }
        if ( ! isset($field_config['data']['driver']) )
        {
            Core::debug()->error('ORM字段配置错误，data属性缺少driver');
            unset($field_config['data']);
            return;
        }
        if ( isset($field_config['data']['mapping']) && ! is_array($field_config['data']['mapping']) )
        {
            Core::debug()->error('ORM字段配置错误，data属性的mapping应该为数组');
            unset($field_config['data']['mapping']);
            return;
        }
        # 数据驱动
        $run = '_parse_' . $field_config['data']['driver'];
        if ( method_exists('OOP_ORM_Parse', $run) )
        {
            $field_config = OOP_ORM_Parse::$run($field_config);
        }
        else
        {
            throw new Exception('指定的解析方法不存在。');
        }
    }

    protected static function _check_object($key, &$field_config)
    {
        if ( ! isset($field_config['object']) )
        {
            return;
        }
        if ( is_string($field_config['object']) )
        {
            $field_config['object'] = array('name' => $field_config['object']);
        }
        elseif ( ! is_array($field_config['object']) )
        {
            Core::debug()->error('ORM字段配置错误，object属性应为数组');
            unset($field_config['object']);
            return;
        }
        if ( ! isset($field_config['object']['name']) )
        {
            Core::debug()->error('ORM字段配置错误，object属性的name必须设置');
            unset($field_config['object']);
            return;
        }
    }

    /**
     * 解析配置
     *
     * @param array $field_config
     * @throws Exception
     * @return array $field_config
     */
    protected static function _parse_database($config)
    {
        if ( ! isset($config['database']) )
        {
            $config['database'] = 'default';
        }

        if ( ! isset($config['tablename']) )
        {
            throw new Exception('配置错误，缺少表名称.');
        }

        return $config;
    }

    /**
     * 格式化数据
     *
     * @param mixed $data
     * @param array $config
     */
    public static function format_data($data, $config)
    {
        foreach ( $config as $v )
        {
            try
            {
                if ( is_array($v) )
                {
                    $fun = 'OOP_ORM_Parse::_format_action_' . array_shift($v);
                    array_unshift($v,$data);
                    $data = call_user_func_array($fun, $v);
                }
                else
                {
                    $fun = '_format_action_' . $v;
                    $data = OOP_ORM_Parse::$fun($data);
                }
            }
            catch ( Exception $e )
            {
                Core::debug()->error('ORM数据解析失败，方法：' . $fun . '。');
            }
        }
        return $data;
    }

    /**
     * 反解数据
     *
     * @param mixed $data
     * @param array $config
     */
    public static function de_format_data($data, $config)
    {
        $config = array_reverse($config);
        foreach ( $config as $v )
        {
            try
            {
                if ( is_array($v) )
                {
                    $fun = 'OOP_ORM_Parse::_de_format_action_' . array_shift($v);
                    array_unshift($v,$data);
                    $data = call_user_func_array($fun, $v);
                }
                else
                {
                    $fun = '_de_format_action_' . $v;
                    $data = OOP_ORM_Parse::$fun($data);
                }
            }
            catch ( Exception $e )
            {
                Core::debug()->error('ORM数据解析失败，方法：' . $fun . '。');
            }
        }

        return $data;
    }

    protected static function _format_action_serialize($data)
    {
        return serialize($data);
    }

    protected static function _format_action_json($data)
    {
        return json_encode($data);
    }

    protected static function _format_action_gzcompress($data)
    {
        return gzcompress($data, 9);
    }

    /**
     * 拼接字符串
     *
     * @param fixed $data
     * @param string $exp_str 默认,分割
     * @param boolean $is_add_both_str 是否在两段加上$exp_str
     *
     * @return string
     */
    protected static function _format_action_implode($data , $exp_str=',' , $is_add_both_str = false )
    {
        $v = (string)implode($exp_str, $data);
        if ( $v==='' )
        {
            return '';
        }
        else
        {
            if ( $is_add_both_str )
            {
                return $exp_str.$v.$exp_str;
            }
            else
            {
                return $v;
            }
        }
    }

    /**
     * 分割字符串
     *
     * @param fixed $data
     * @param string $exp_str 默认,分割
     * @param boolean $is_add_both_str 是否在两段加上$exp_str
     *
     * @return array
     */
    protected static function _de_format_action_implode($data , $exp_str=',' , $is_add_both_str = false )
    {
        $data = (string)$data;
        if ( $is_add_both_str )
        {
            $data = trim( $data , $exp_str );
        }
        if ( empty($data) )
        {
            return array();
        }
        return explode($exp_str,$data);
    }

    protected static function _de_format_action_serialize($data)
    {
        return @unserialize((string)$data);
    }

    protected static function _de_format_action_json($data)
    {
        return @json_decode($data, true);
    }

    protected static function _de_format_action_gzcompress($data)
    {
        return @gzuncompress($data);
    }

    /**
     * 获取缓存数据
     *
     * @param array $config
     */
    public static function get_cache_data($key, $where, $config)
    {
        return false;
    }

    public static function set_cache_data($key, $where, $config, $data)
    {
        return false;
    }

    public static function get_data($config)
    {
        $fun = '_get_data_' . $config['driver'];
        return OOP_ORM_Parse::$fun($config);
    }

    /**
     * 根据指定配置获取数据
     *
     * @param array $config
     */
    protected static function _get_data_database($config)
    {
        $data = Database::instance($config['database'])->from($config['tablename']);
        if ( @$config['where'] )
        {
            $data->where($config['where']);
        }
        if ( @$config['limit'] )
        {
            $data->limit($config['limit']);
        }
        if ( @$config['offset'] )
        {
            $data->offset($config['offset']);
        }
        if ( @$config['order_by'] )
        {
            foreach ( $config['order_by'] as $k => $v )
            {
                $data->order_by($k, $v == 'DESC' ? 'DESC' : 'ASC');
            }
        }
        $result = $data->get();
        if ( @$config['result_single_row'] )
        {
            $result = $result->current();
        }
        else
        {
            $result = $result->as_array();
        }

        return $result;
    }

    public static function get_object_field_data($obj, $callback_fun = null)
    {
        if ( $callback_fun )
        {
            $obj = $obj->$callback_fun();
        }
        elseif ( $obj instanceof OOP_ORM_Data )
        {
            $obj = $obj->get_field_data();
        }
        elseif ( $obj instanceof stdClass )
        {
            $obj = (array)$obj;
        }
        elseif ( $obj instanceof OOP_ORM )
        {
            # OOP_ORM对象无法作用于实际数据
            return false;
        }
        elseif ( method_exists($obj, 'getArrayCopy') )
        {
            $obj = $obj->getArrayCopy();
        }
        elseif ( method_exists($obj, '__toString') )
        {
            $obj = $obj->__toString();
        }
        else
        {
            # 对于未知且没有设置回调方法的对象，忽略
            return false;
        }
        return $obj;
    }

    public static function & key_array( &$arr, $key )
    {
        if ( strlen($key) )
        {
            $kk = explode('.', $key);
            $k = array_shift($kk);
            $newkey = implode('.', $kk);
            return OOP_ORM_Parse::key_array($arr[$k], $newkey);
        }
        else
        {
            return $arr;
        }
    }

    /**
     * 根据字段ORM配置获取ORM数据
     *
     * @param OOP_ORM_Data $data_obj
     * @param array $orm_config
     * @throws Exception
     * @return mixed
     */
    public static function get_orm_data_by_config(OOP_ORM_Data $data_obj, $orm_config, $index)
    {
        # 映射ORM对象
        $obj_name = 'ORM_' . $orm_config['name'] . '_Finder';
        if ( ! class_exists($obj_name, true) )
        {
            throw new Exception('指定的ORM:' . $orm_config['name'] . '不存在！');
        }
        $obj = new $obj_name();

        if ( isset( $orm_config['cache'] ) )
        {
            # 设置了缓存
            $c_config = $orm_config;
            foreach ( $c_config['mapping'] as & $item )
            {
                $item = $data_obj->$item;
            }
            $data = OOP_ORM_Parse::get_orm_cache_data($data_obj , $index , $c_config);
            if ( false!==$data )
            {
                return $data;
            }

            $can_try_get_gourps_data = false;
        }
        else
        {
            # 是否可以批量取对象集里的数据
            $can_try_get_gourps_data = true;
        }

        # 支持OFFSET
        if ( isset($orm_config['offset']) )
        {
            $obj->offset($orm_config['offset']);
            if ( $orm_config['offset'] > 0 )
            {
                $can_try_get_gourps_data = false; //有OFFSET的话，不能同时取多条数据
            }
        }

        # 支持GROUP BY
        if ( isset($orm_config['group_by']) )
        {
            foreach ($orm_config['group_by'] as $item)
            {
                $obj->group_by($item);
            }
            $can_try_get_gourps_data = false;
        }

        # 支持ORDER BY
        if ( isset($orm_config['order_by']) )
        {
            foreach ($orm_config['order_by'] as $key=>$item)
            {
                $obj->order_by($key , $item);
            }
            $can_try_get_gourps_data = false;
        }

        # 支持IN
        if ( isset($orm_config['in']) )
        {
            foreach ($orm_config['in'] as $key=>$item)
            {
                $obj->in($key , $item);
            }
            $can_try_get_gourps_data = false;
        }

        # 获取ORM单条数据
        if ( $can_try_get_gourps_data && $orm_config['return'] == OOP_ORM::PARAM_RETURN_SINGLE )
        {
            # 对于单条数据，可以尝试批量获取，以优化SQL性能
            $group_data = $data_obj->get_group_data();
            if ( $group_data )
            {
                $obj->limit(count($group_data));
                foreach ( $orm_config['mapping'] as $k => $v )
                {
                    $tmpdata = array(); //记录临时数据
                    foreach ( $group_data as $i => $item )
                    {
                        # 当前字段需要符合的条件
                        $iswhere = true;
                        if ( isset($orm_config['where']) )
                        {
                            foreach ( $orm_config['where'] as $wk => $wv )
                            {
                                if ( $item->$wk != $wv )
                                {
                                    $iswhere = false;
                                }
                            }
                        }
                        if ( $iswhere )
                        {
                            $tmpdata[] = $item->$v;
                        }
                    }
                    $tmpdata = array_unique($tmpdata);
                    if ( $tmpdata )
                    {
                        $obj->in($k, $tmpdata);
                    }
                }

                if ( $tmpdata )
                {
                    # 如果有临时数据，则表明可以进行批量获取
                    if ( isset($orm_config['where']) ) foreach ( $orm_config['where'] as $k => $v )
                    {
                        # 额外的一些条件
                        $obj->where($k, $v);
                    }
                    $all_data = $obj->find();
                }
                else
                {
                    $all_data = array();
                }

                # 整理data
                $tmp = array();
                foreach ( $all_data as $item )
                {
                    $tmpkey = '';
                    foreach ( $orm_config['mapping'] as $k => $v )
                    {
                        $tmpkey .= '|' . $k . '_' . $item->$k;
                    }
                    $tmp[$tmpkey] = $item;
                }

                # 将获取来的数据赋值给各个对象
                foreach ( $group_data as $item )
                {
                    $tmpkey = '';
                    foreach ( $orm_config['mapping'] as $k => $v )
                    {
                        $tmpkey .= '|' . $k . '_' . $item->$v;
                    }
                    if ( $item == $data_obj )
                    {
                        # 当前对象
                        $data = isset($tmp[$tmpkey]) ? $tmp[$tmpkey] : null;
                    }
                    else
                    {
                        $item->offsetSet($index, isset($tmp[$tmpkey]) ? $tmp[$tmpkey] : null);
                    }
                }

                if ( isset($data) )
                {
                    # 已经获取到当前对象，直接返回
                    return $data;
                }
            }
        }

        $orm_where = array();
        # 处理mapping
        if ( $orm_config['mapping'] )
        {
            foreach ( $orm_config['mapping'] as $k => $v )
            {
                $orm_where[$k] = $data_obj->$v;
            }
        }
        if ( isset($orm_config['where']) )
        {
            $orm_where = $orm_config['where'] + $orm_where;
        }

        if ( $orm_where )
        {
            # 设置autowhere
            $obj->set_auto_where($orm_where);
        }

        if ( $orm_config['limit'] )
        {
            $obj->limit($orm_config['limit']);
        }

        if ( $orm_config['return'] == OOP_ORM::PARAM_RETURN_SINGLE )
        {
            # 获取单条数据
            $data = $obj->find()->current();

        }
        elseif ( $orm_config['return'] == OOP_ORM::PARAM_RETURN_GROUP )
        {
            # 获取一组数据
            $data = $data = $obj->find();
        }
        else
        {
            # 当前ORM对象
            $data = $obj;
        }

        if ( isset( $orm_config['cache'] ) )
        {
            # 设置缓存
            OOP_ORM_Parse::set_orm_cache_data($data_obj , $index , $c_config , $data);
        }

        return $data;
    }

    /**
     * 根据offset及配额获取缓存数据（如果有）
     *
     * @param string $index
     * @param array $config
     * @return fiexd
     */
    public static function get_offset_cache_data( OOP_ORM_Data $data_obj , $index , $cache_config )
    {
        # 获取一个key
        $key = OOP_ORM_Parse::get_offset_cache_key($data_obj , $index);

        return Cache::instance($cache_config['driver_config'])->get($key);
    }


    /**
     * 根据offset及配额设置缓存数据
     *
     * @param string $index
     * @param array $config
     * @param data
     *
     * @return boolean
     */
    public static function set_offset_cache_data( OOP_ORM_Data $data_obj , $index , $cache_config , $data )
    {
        # 获取一个key
        $key = OOP_ORM_Parse::get_offset_cache_key($data_obj, $index);

        return Cache::instance($cache_config['driver_config'])->set($key , $data , isset($cache_config['expire'])?$cache_config['expire']:3600 , $cache_config['expire_type'] );
    }

    /**
     * 根据offset及配额获取缓存数据（如果有）
     *
     * @param string $index
     * @param array $config
     * @return fiexd
     */
    public static function delete_offset_cache_data( OOP_ORM_Data $data_obj , $index , $cache_config )
    {
        # 获取一个key
        $key = OOP_ORM_Parse::get_offset_cache_key($data_obj , $index);

        # 清除缓存
        return Cache::instance($cache_config['driver_config'])->delete($key);
    }

    /**
     * 根据index获取一个唯一的缓存key
     * @param string $index
     * @return string
     */
    public static function get_offset_cache_key( OOP_ORM_Data $data_obj , $index )
    {
        $id_field_name = $data_obj->id_field_name();
        $id_value = $data_obj->$id_field_name;
        return '_ORM_OFFSET_CACHE_' . get_class($data_obj) . '_' . $id_value . '_' . $index;
    }


    /**
     * 根据offset及配额获取缓存数据（如果有）
     *
     * @param string $index
     * @param array $config
     * @return fiexd
     */
    public static function get_orm_cache_data( OOP_ORM_Data $data_obj , $index , $config )
    {
        # 获取一个key
        $key = OOP_ORM_Parse::get_orm_cache_key($data_obj , $index, $config);

        return Cache::instance($config['cache']['driver_config'])->get($key);
    }


    /**
     * 根据offset及配额设置缓存数据
     *
     * @param string $index
     * @param array $config
     * @param data
     *
     * @return boolean
     */
    public static function set_orm_cache_data( OOP_ORM_Data $data_obj , $index , $config , $data )
    {
        # 获取一个key
        $key = OOP_ORM_Parse::get_orm_cache_key($data_obj, $index, $config);

        return Cache::instance($config['cache']['driver_config'])->set($key , $data , isset($config['cache']['expire'])?$config['cache']['expire']:3600 , $config['cache']['expire_type'] );
    }

    /**
     * 根据offset及配额获取缓存数据（如果有）
     *
     * @param string $index
     * @param array $config
     * @return fiexd
     */
    public static function delete_orm_cache_data( OOP_ORM_Data $data_obj , $index , $config )
    {
        # 获取一个key
        $key = OOP_ORM_Parse::get_orm_cache_key($data_obj , $index , $config);

        # 清除缓存
        return Cache::instance($config['cache']['driver_config'])->delete($key);
    }

    /**
     * 根据index获取一个唯一的缓存key
     * @param string $index
     * @return string
     */
    public static function get_orm_cache_key( OOP_ORM_Data $data_obj , $index , $config )
    {
        $id_field_name = $data_obj->id_field_name();
        $id_value = 'config:' . md5(serialize($config));
        return '_ORM_ORMDATA_CACHE_' . get_class($data_obj) . '_' . $id_value . '_' . $index;
    }
}